﻿using System;
using System.ServiceModel;
using FluentAssertions;
using FluentAssertions.Assertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using ServiceProxy;

namespace CalculatorTwo.UnitTests
{
    [TestClass]
    public class CalculatorTests
    {
        [TestMethod]
        public void Add_WhenClientIsMocked_ShouldMockBeCalled()
        {
            //Arrange
            var client = new Mock<ICalculatorChannel>();
            client.Setup(c => c.add(It.IsAny<float>(), It.IsAny<float>())).Returns(42);

            var calculator = new Calculator(client.Object);

            //Act
            float response = calculator.Add(30, 12);

            //Assert
            client.Verify(c => c.add(30, 12), Times.Once());
            client.Verify(c => c.Close(), Times.Once());
            response.Should().BeApproximately(42, 0);
        }

        [TestMethod]
        public void Add_WhenCommunicationExceptionIsThrownByCloseMethod_ShouldClientAbortBeCalled()
        {
            //Arrange
            var client = new Mock<ICalculatorChannel>();
            client.Setup(c => c.add(It.IsAny<float>(), It.IsAny<float>())).Returns(42);
            client.Setup(c => c.Close()).Throws(new CommunicationException());

            var calculator = new Calculator(client.Object);

            //Act
            calculator.Add(30, 12);

            //Assert
            client.Verify(c => c.add(30, 12), Times.Once());
            client.Verify(c => c.Abort(), Times.Once());
        }

        [TestMethod]
        public void Add_WhenTimeoutExceptionIsThrownByCloseMethod_ShouldClientAbortBeCalled()
        {
            //Arrange
            var client = new Mock<ICalculatorChannel>();
            client.Setup(c => c.add(It.IsAny<float>(), It.IsAny<float>())).Returns(42);
            client.Setup(c => c.Close()).Throws(new TimeoutException());

            var calculator = new Calculator(client.Object);

            //Act
            calculator.Add(30, 12);

            //Assert
            client.Verify(c => c.add(30, 12), Times.Once());
            client.Verify(c => c.Abort(), Times.Once());
        }

        [TestMethod]
        public void Add_WhenClientIsMockedAndExceptionIsThrown_ShouldExceptionBeThrown()
        {
            //Arrange
            var client = new Mock<ICalculatorChannel>();
            client.Setup(c => c.add(It.IsAny<float>(), It.IsAny<float>())).Throws(new ArgumentException());

            var calculator = new Calculator(client.Object);

            //Act
            Action addAction = () => calculator.Add(30, 12);

            //Assert
            addAction.ShouldThrow<ArgumentException>();
        }
    }
}